﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace UnityEngine.UI
{
    /// <summary>
    /// 1D摇杆，只有一个方向的操纵杆，会返回 -1 ~ 1的值
    /// </summary>
    public class Joystick1D : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
    {
        [System.Serializable] public class Joystick1DEvent : UnityEvent<float> { }
        public bool freeMode = true;
        public float maxWidth = 100; //Handle 移动最大半径

        public Joystick1DEvent OnValueChanged = new Joystick1DEvent(); //事件
        [SerializeField] private RectTransform backGround, handle, direction; //摇杆背景、摇杆手柄、方向指引
        private float joysticValue = 0;
        public float Value => joysticValue;
        public bool IsDraging { get; private set; }
        private int touchId = -1;

        private void Awake()
        {
            direction.gameObject.SetActive(false);
        }

        public void OnDrag(PointerEventData eventData)
        {
            if (touchId != eventData.pointerId) return;
            Vector2 backGroundPos;

            if (freeMode)
            {
                backGroundPos = (null == eventData.pressEventCamera) ? backGround.position : eventData.pressEventCamera.WorldToScreenPoint(backGround.position);
            }
            else
            {
                backGroundPos = this.transform.position;
            }

            Vector2 direction = eventData.position - backGroundPos; //得到方位盘中心指向光标的向量
            float distance = Vector3.Magnitude(direction); //获取向量的长度
            float radius = Mathf.Clamp(distance, 0, maxWidth); //锁定 Handle 半径
            var pos = direction.normalized * radius; //更新 Handle 位置
            handle.localPosition = new Vector2(pos.x, 0);
            UpdateDirectionArrow(direction);
        }

        public void OnPointerDown(PointerEventData eventData)
        {
            if (touchId != -1) return;
            touchId = eventData.pointerId;

            if (freeMode)
            {
                Vector3 backGroundPos;
                backGroundPos = new Vector3()
                {
                    x = eventData.position.x,
                    y = eventData.position.y,
                    z = (null == eventData.pressEventCamera) ? backGround.position.z :
                  eventData.pressEventCamera.WorldToScreenPoint(backGround.position).z
                };
                backGround.position = (null == eventData.pressEventCamera) ? backGroundPos : eventData.pressEventCamera.ScreenToWorldPoint(backGroundPos);
            }
            else
            {
                backGround.position = this.transform.position;
            }
            IsDraging = true;

        }

        private void UpdateDirectionArrow(Vector2 position)
        {
            if (position.x != 0 || position.y != 0)
            {
                direction.gameObject.SetActive(true);
                direction.localScale = new Vector3((position.x > 0 ? 1 : -1), 1, 1);
            }
        }

        public void OnPointerUp(PointerEventData eventData)
        {
            if (touchId != eventData.pointerId) return;
            touchId = -1;
            direction.gameObject.SetActive(false);
            backGround.localPosition = Vector3.zero;
            handle.localPosition = Vector3.zero;
            joysticValue = 0;
            IsDraging = false;
        }

        private void OnJoystickCancel()
        {
            touchId = -1;
            direction.gameObject.SetActive(false);
            backGround.localPosition = Vector3.zero;
            handle.localPosition = Vector3.zero;
            joysticValue = 0;
            IsDraging = false;
        }

        public static bool GetTouchByFingerId(int fingerId, out Touch touch)
        {
            touch = default(Touch);
            if (Input.touchCount == 0 || fingerId < 0)
            {
                return false;
            }
            for (int i = 0; i < Input.touchCount; i++)
            {
                if (Input.GetTouch(i).fingerId == fingerId)
                {
                    touch = Input.GetTouch(i);
                    return true;
                }
            }

            return false;
        }

        private Touch _touch;
        // Update is called once per frame
        void Update()
        {
            if (IsDraging)
            {
                if (touchId != -1)
                {
                    if (GetTouchByFingerId(touchId, out _touch))
                    {
                        switch (_touch.phase)
                        {
                            case TouchPhase.Ended:
                            case TouchPhase.Canceled:
                                OnJoystickCancel();
                                return;
                        }
                    }
                    else
                    {
                        OnJoystickCancel();
                    }
                }

                joysticValue = handle.anchoredPosition.x / maxWidth;
                OnValueChanged.Invoke(joysticValue);
            }

        }


    }
}